﻿using Google.Protobuf.WellKnownTypes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Minio;
using Minio.DataModel.Args;
using PmSoft.Exceptions;
using System.Security.AccessControl;

namespace PmSoft.FileStorage;

/// <summary>
/// MinIo分布式对象存储服务
/// </summary>
[UseDI(ServiceLifetime.Scoped)]
public class MinIoStorage : IFileStorage
{
    private readonly MinIoOptions minIoOptions;
    private readonly IMinioClient client;

    public MinIoStorage(IOptions<MinIoOptions> options)
    {
        this.minIoOptions = options.Value;
        client = new MinioClient()
                          .WithEndpoint(minIoOptions.EndPoint)
                          .WithCredentials(minIoOptions.AccessKey,
                                   minIoOptions.SecretKey);
        if (minIoOptions.WithSSL)
            client = client.WithSSL();
        client.Build();
    }

    public async Task<string> PutFileAsync(string bucketName, string fileName, Stream stream)
    {
        PutObjectArgs args = new PutObjectArgs()
        .WithBucket(bucketName)
                                .WithObject(fileName)
    .WithStreamData(stream)
        .WithObjectSize(stream.Length)
                                .WithContentType("application/octet-stream")
                                .WithHeaders(new Dictionary<string, string>())
                                .WithServerSideEncryption(null);
        var response = await client.PutObjectAsync(args);
        return response.ObjectName;
    }

    public async Task<string> PutFileAsync(string bucketName, string fileName, byte[] fileData)
    {
        using MemoryStream ms = new MemoryStream(fileData);
        return await PutFileAsync(bucketName, fileName, ms);
    }

    public async Task<byte[]> GetFileAsync(string bucketName, string fileName)
    {
        byte[] buffer = new byte[0];
        StatObjectArgs args = new StatObjectArgs()
                             .WithBucket(bucketName)
                             .WithObject(fileName)
                             .WithServerSideEncryption(null);

        var objectStat = await client.StatObjectAsync(args);

        using (MemoryStream memoryStream = new MemoryStream())
        {
            GetObjectArgs objArgs = new GetObjectArgs()
                                .WithBucket(bucketName)
                                .WithObject(fileName)
                                .WithCallbackStream((stream) =>
                                {
                                    stream.CopyTo(memoryStream);
                                })
                                .WithOffsetAndLength(0, objectStat.Size)
                                .WithServerSideEncryption(null);
            await client.GetObjectAsync(objArgs);
            buffer = memoryStream.ToArray();
        }
        return buffer;
    }

    public Task<string> PresignedGetObject(string bucketName, string fileName)
    {
        try
        {
            //Dictionary<string, string> reqParams = new Dictionary<string, string> { { "response-content-type", "application/json" } };
            PresignedGetObjectArgs args = new PresignedGetObjectArgs()
                                                    .WithBucket(bucketName)
                                                    .WithObject(fileName)
            //.WithHeaders(reqParams)
                                                    .WithExpiry(1000);

            //.WithRequestDate(DateTime.Now.ToUniversalTime());

            return client.PresignedGetObjectAsync(args);
        }
        catch (Exception e)
        {
            throw new BusinessException(e.Message);
        }
    }
}
